home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2005 March
/
CMCD0305.ISO
/
Software
/
Shareware
/
Utilitare
/
emu
/
Emu8086_Setup_307c.exe
/
{app}
/
Samples
/
float.asm
< prev
next >
Wrap
Assembly Source File
|
2002-08-02
|
12KB
|
461 lines
TITLE CALCULATE EQUATION
; AUTHOR Yuri Margolin
; http://mysoft.s5.com/
; DATE 24/01/01
; VERSION 1.12
; This program calculates
; linear equation: ax + b = 0
; The result is printed with
; floating point.
; For example: a = 7, b = 2
; x = -0.28571428....
; Directive to select
; "make EXE" by default when
; source file is compiled:
#MAKE_EXE#
DSEG SEGMENT 'DATA'
CR EQU 13
LF EQU 10
NEW_LINE EQU 13, 10, '$'
MESS0 DB 'Calculation of ax + b = 0', NEW_LINE
MESS1 DB 'ENTER a (-32768..32767)!', NEW_LINE
MESS2 DB LF, CR, 'ENTER b (-32768..32767)!', NEW_LINE
MESS3 DB CR, LF, CR, LF, 'Data:', '$'
MESS4 DB CR, LF, ' a = ', '$'
MESS5 DB CR, LF, ' b = ', '$'
MESS6 DB CR, LF, 'Result: ', CR, LF, ' x = ', '$'
MESS7 DB CR, LF, CR, LF, 'NO SOLUTION!', NEW_LINE
MESS8 DB CR, LF, CR, LF, 'INFINITE NUMBER OF SOLUTIONS!', NEW_LINE
ERROR DB CR, LF, 'THE NUMBER IS OUT OF RANGE!', NEW_LINE
TWICE_NL DB NEW_LINE, NEW_LINE
make_minus DB ? ; used as a flag in procedures.
a DW ?
b DW ?
ten DW 10 ; used as multiplier.
four DW 4 ; used as divider.
DSEG ENDS
SSEG SEGMENT STACK 'STACK'
DW 100h DUP(?)
SSEG ENDS
CSEG SEGMENT 'CODE'
;*******************************************************************
START PROC FAR
; Store return address to OS:
PUSH DS
XOR AX, AX
PUSH AX
; Set segment registers:
MOV AX, DSEG
MOV DS, AX
MOV ES, AX
; Welcome message:
LEA DX, MESS0
CALL PUTS ; Display the message.
; Ask for 'a' :
LEA DX, MESS1
CALL PUTS ; Display the message.
CALL SCAN_NUM ; Input the number into CX.
MOV a, CX
; Ask for 'b' :
LEA DX, MESS2
CALL PUTS ; Display the message.
CALL SCAN_NUM ; Input the number into CX.
MOV b, CX
; Print the data:
LEA DX, MESS3
CALL PUTS
LEA DX, MESS4
CALL PUTS
MOV AX, a
CALL PRINT_NUM ; print AX.
LEA DX, MESS5
CALL PUTS
MOV AX, b
CALL PRINT_NUM ; print AX.
; Check data:
CMP a, 0
JNE soluble ; jumps when a<>0.
CMP b, 0
JNE no_solution ; jumps when a=0 AND b<>0.
JMP infinite ; jumps when a=0 AND b=0.
soluble:
; Calculate the solution:
; ax + b = 0 -> ax = -b -> x = -b/a
NEG b
MOV AX, b
XOR DX, DX
; check the sign, make DX:AX negative if AX is negative:
CMP AX, 0
JNS not_singned
NOT DX
not_singned:
MOV BX, a ; divider is in BX.
; '-b' is in DX:AX.
; 'a' is in BX.
IDIV BX ; AX = DX:AX / BX (DX - remainder).
; 'x' is in AX.
; remainder is in DX.
PUSH DX ; store the remainder.
LEA DX, MESS6
CALL PUTS
POP DX
; print 'x' as float:
; AX - whole part
; DX - remainder
; BX - divider
CALL PRINT_FLOAT
JMP end_prog
no_solution:
LEA DX, MESS7
CALL PUTS
JMP end_prog
infinite:
LEA DX, MESS8
CALL PUTS
end_prog:
LEA DX, TWICE_NL
CALL PUTS
RET
START ENDP
;***************************************************************
; Prints number in AX and it's fraction in DX.
; used to print remainder of 'DIV/IDIV BX'.
; AX - whole part.
; DX - remainder.
; BX - the divider that was used to get the remainder from divident.
PRINT_FLOAT PROC NEAR
PUSH CX
PUSH DX
; because the remainder takes the sign of divident
; its sign should be inverted when divider is negative
; (-) / (-) = (+)
; (+) / (-) = (-)
CMP BX, 0
JNS div_not_signed
NEG DX ; make remainder positive.
div_not_signed:
; PRINT_NUM procedure does not print the '-'
; when the whole part is '0' (even if the remainder is
; negative) this code fixes it:
CMP AX, 0
JNE checked ; AX<>0
CMP DX, 0
JNS checked ; AX=0 and DX>=0
PUSH DX
MOV DL, '-'
CALL WRITE_CHAR ; print '-'
POP DX
checked:
; print whole part:
CALL PRINT_NUM
; if remainder=0, then no need to print it:
CMP DX, 0
JE done
PUSH DX
; print dot after the number:
MOV DL, '.'
CALL WRITE_CHAR
POP DX
; print digits after the dot:
MOV CX, 15 ; max digits after the dot.
CALL PRINT_FRACTION
done:
POP DX
POP CX
RET
PRINT_FLOAT ENDP
;***************************************************************
; Prints DX as fraction of division by BX.
; DX - remainder.
; BX - divider.
; CX - maximum number of digits after the dot.
PRINT_FRACTION PROC NEAR
PUSH AX
PUSH DX
next_fraction:
; check if all digits are already printed:
CMP CX, 0
JZ end_rem
DEC CX ; decrease digit counter.
; when remainder is '0' no need to continue:
CMP DX, 0
JE end_rem
MOV AX, DX
XOR DX, DX
CMP AX, 0
JNS not_sig1
NOT DX
not_sig1:
IMUL ten ; DX:AX = AX * 10
IDIV BX ; AX = DX:AX / BX (DX - remainder)
PUSH DX ; store remainder.
MOV DX, AX
CMP DX, 0
JNS not_sig2
NEG DX
not_sig2:
ADD DL, 30h ; convert to ASCII code.
CALL WRITE_CHAR ; print DL.
POP DX
JMP next_fraction
end_rem:
POP DX
POP AX
RET
PRINT_FRACTION ENDP
;***************************************************************
; This procedure prints number in AX,
; used with PRINT_NUMX to print "0" and sign.
; this procedure also stores the original AX,
; that is modified by PRINT_NUMX.
PRINT_NUM PROC NEAR
PUSH DX
PUSH AX
CMP AX, 0
JNZ not_zero
MOV DL, '0'
CALL WRITE_CHAR
JMP printed
not_zero:
; the check SIGN of AX,
; make absolute if it's negative:
CMP AX, 0
JNS positive
NEG AX
MOV DL, '-'
CALL WRITE_CHAR
positive:
CALL PRINT_NUMX
printed:
POP AX
POP DX
RET
PRINT_NUM ENDP
;***************************************************************
; Prints out a number in AX (not just a single digit)
; allowed values from 1 to 65535 (FFFF)
; (result of /10000 should be the left digit or "0").
; modifies AX (after the procedure AX=0)
PRINT_NUMX PROC NEAR
PUSH BX
PUSH CX
PUSH DX
; flag to prevent printing zeros before number:
MOV CX, 1
MOV BX, 10000 ; 2710h - divider.
; Check if AX is zero, if zero go to end_show
CMP AX, 0
JZ end_show
begin_print:
; check divider (if zero go to end_show):
CMP BX,0
JZ end_show
; avoid printing zeros before number:
CMP CX, 0
JE calc
; if AX<BX then result of DIV will be zero:
CMP AX, BX
JB skip
calc:
XOR CX, CX ; set flag.
XOR DX, DX
DIV BX ; AX = DX:AX / BX (DX=remainder).
; print last digit
; AH is always ZERO, so it's ignored
PUSH DX
MOV DL, AL
ADD DL, 30h ; convert to ASCII code.
CALL WRITE_CHAR
POP DX
MOV AX, DX ; get remainder from last div.
skip:
; calculate BX=BX/10
PUSH AX
XOR DX, DX
MOV AX, BX
DIV ten ; AX = DX:AX / 10 (DX=remainder).
MOV BX, AX
POP AX
JMP begin_print
end_show:
POP DX
POP CX
POP BX
RET
PRINT_NUMX ENDP
;***************************************************************
; Displays the message (DX-address)
PUTS PROC NEAR
PUSH AX
MOV AH, 09h
INT 21h
POP AX
RET
PUTS ENDP
;*******************************************************************
; Reads char from the keyboard into AL
; (MODIFIES AX!!!)
READ_CHAR PROC NEAR
MOV AH, 01h
INT 21h
RET
READ_CHAR ENDP
;***************************************************************
; Gets the multi-digit SIGNED number from the keyboard,
; Result is stored in CX
SCAN_NUM PROC NEAR
PUSH DX
PUSH AX
XOR CX, CX
; reset flag:
MOV make_minus, 0
next_digit:
CALL READ_CHAR
; check for MINUS:
CMP AL, '-'
JE set_minus
; check for ENTER key:
CMP AL, CR
JE stop_input
; multiply CX by 10 (first time the result is zero)
PUSH AX
MOV AX, CX
MUL ten ; DX:AX = AX*10
MOV CX, AX
POP AX
; check if the number is too big
; (result should be 16 bits)
CMP DX, 0
JNE out_of_range
; convert from ASCII code:
SUB AL, 30h
; add AL to CX:
XOR AH, AH
ADD CX, AX
JC out_of_range ; jump if the number is too big.
JMP next_digit
set_minus:
MOV make_minus, 1
JMP next_digit
out_of_range:
LEA DX, ERROR
CALL PUTS
stop_input:
; check flag:
CMP make_minus, 0
JE not_minus
NEG CX
not_minus:
POP AX
POP DX
RET
SCAN_NUM ENDP
;***************************************************************
; Prints out single char (ascii code should be in DL)
WRITE_CHAR PROC NEAR
PUSH AX
MOV AH, 02h
INT 21h
POP AX
RET
WRITE_CHAR ENDP
;***************************************************************
CSEG ENDS
END START